local M = class("MsgDecoder")

function M:ctor(protos)
	self.protos = protos or {}
end

function MsgDecoder.setProtos(protos)
    if protos) then
        self.protos = protos
    end
end

function MsgDecoder.decode(route, buf)
    local protos = self.protos[route]
    buffer = buf
    offset = 0
    if protos then
        return decodeMsg({}, protos, buffer.length)
    end
    return nil
end

function decodeMsg(msg, protos, length)
    while(offset<length)
        local head = getHead()
        local type = head.type
        local tag = head.tag
        local name = protos.__tags[tag]

        switch(protos[name].option)
            case 'optional' :
            case 'required' :
                msg[name] = decodeProp(protos[name].type, protos)
                break
            case 'repeated' :
                if not msg[name])
                    msg[name] = []
                
                decodeArray(msg[name], protos[name].type, protos)
                break
        	end
    end
    return msg
end

function isFinish(msg, protos)
    return (not protos.__tags[peekHead().tag])
end

function getHead()
    local tag = codec.decodeUInt32(getBytes())
    return {
        type : tag&0x7,
        tag : tag>>3
    }
end


function peekHead()
    local tag = codec.decodeUInt32(peekBytes())
    return {
        type : tag&0x7,
        tag : tag>>3
    }
end

function decodeProp(type, protos)
    switch(type)
        case 'uInt32':
            return codec.decodeUInt32(getBytes())
        case 'int32' :
        case 'sInt32' :
            return codec.decodeSInt32(getBytes())
        case 'float' :
            local float = codec.decodeFloat(buffer, offset)
            offset += 4
            return float
        case 'double' :
            local double = codec.decodeDouble(buffer, offset)
            offset += 8
            return double
        case 'string' :
            local length = codec.decodeUInt32(getBytes())
            local str =  codec.decodeStr(buffer, offset, length)
            offset += length
            return str
        default :
            if protos and protos.__messages[type]) then
                local length = codec.decodeUInt32(getBytes())
                local msg = 
                decodeMsg(msg, protos.__messages[type], offset+length)
                return msg
            end
            break
		end
	end
end

function decodeArray(array, type, protos)
    if util.isSimpleType(type)) then
        local length = codec.decodeUInt32(getBytes())
        for(local i = 0 i < length i++) do
            array.push(decodeProp(type))
        end
    else
        array.push(decodeProp(type, protos))
    end
end

function getBytes(flag)
    local bytes = []
    local pos = offset
    flag = flag or false

    local b

    do
        b = buffer[pos]
        bytes.push(b)
        pos++
    while(b >= 128)

    if not flag) then
        offset = pos
    end
    
    return bytes
end

function peekBytes()
    return getBytes(true)
end

return M

--(function (exports)
--    local protobuf = exports.Protobuf
--    local MsgDecoder = exports.Protobuf.decoder = 
--
--    local codec = protobuf.codec
--    local util = protobuf.util
--
--    local buffer
--    local offset = 0
--
--    MsgDecoder.init(protos)
--        self.protos = protos or 
--    
--
--    MsgDecoder.setProtos(protos)
--        if protos)
--            self.protos = protos
--        
--    
--
--    MsgDecoder.decode(route, buf)
--        local protos = self.protos[route]
--
--        buffer = buf
--        offset = 0
--        if protos)
--            return decodeMsg(, protos, buffer.length)
--        
--
--        return nil
--    
--
--    function decodeMsg(msg, protos, length)
--        while(offset<length)
--            local head = getHead()
--            local type = head.type
--            local tag = head.tag
--            local name = protos.__tags[tag]
--
--            switch(protos[name].option)
--                case 'optional' :
--                case 'required' :
--                    msg[name] = decodeProp(protos[name].type, protos)
--                    break
--                case 'repeated' :
--                    if not msg[name])
--                        msg[name] = []
--                    
--                    decodeArray(msg[name], protos[name].type, protos)
--                    break
--            
--        
--
--        return msg
--    
--
--    function isFinish(msg, protos)
--        return (not protos.__tags[peekHead().tag])
--    
--
--    function getHead()
--        local tag = codec.decodeUInt32(getBytes())
--
--        return 
--            type : tag&0x7,
--            tag : tag>>3
--        
--    
--
--    function peekHead()
--        local tag = codec.decodeUInt32(peekBytes())
--
--        return 
--            type : tag&0x7,
--            tag : tag>>3
--        
--    
--
--    function decodeProp(type, protos)
--        switch(type)
--            case 'uInt32':
--                return codec.decodeUInt32(getBytes())
--            case 'int32' :
--            case 'sInt32' :
--                return codec.decodeSInt32(getBytes())
--            case 'float' :
--                local float = codec.decodeFloat(buffer, offset)
--                offset += 4
--                return float
--            case 'double' :
--                local double = codec.decodeDouble(buffer, offset)
--                offset += 8
--                return double
--            case 'string' :
--                local length = codec.decodeUInt32(getBytes())
--
--                local str =  codec.decodeStr(buffer, offset, length)
--                offset += length
--
--                return str
--            default :
--                if protos and protos.__messages[type])
--                    local length = codec.decodeUInt32(getBytes())
--                    local msg = 
--                    decodeMsg(msg, protos.__messages[type], offset+length)
--                    return msg
--                
--                break
--        
--    
--
--    function decodeArray(array, type, protos)
--        if util.isSimpleType(type))
--            local length = codec.decodeUInt32(getBytes())
--
--            for(local i = 0 i < length i++)
--                array.push(decodeProp(type))
--            
--        else
--            array.push(decodeProp(type, protos))
--        
--    
--
--    function getBytes(flag)
--        local bytes = []
--        local pos = offset
--        flag = flag or false
--
--        local b
--
--        do
--            b = buffer[pos]
--            bytes.push(b)
--            pos++
--        while(b >= 128)
--
--        if not flag)
--            offset = pos
--        
--        return bytes
--    
--
--    function peekBytes()
--        return getBytes(true)
--    
--
--)(HY.net)